package com.googlecode.raisinrefinery.grapevinescanner;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.Map;

public class Preprocessor {

    public static ByteArrayOutputStream preprocess(InputStream is,
                StringBuilder firstComment, Map<String,String> defines)
                throws IOException, PreprocessException
        {
            boolean inSingleQuotes = false;
            boolean inString = false;
            boolean inComment = false;
            boolean inDirective = false;
            boolean hadSlash = false;
            boolean hadStar = false;
            boolean hadBackSlash = false;
            ByteArrayOutputStream out = new ByteArrayOutputStream();
            boolean seenFirstComment = (firstComment == null);
            StringBuilder directiveBuffer = new StringBuilder();
            for (int c = is.read(); c != -1; c = is.read()) {
                if (inComment) {
                    if (c == '*') {
                        if (hadStar && !seenFirstComment) {
                            firstComment.append('*');
                        }
                        hadStar = true;
                    } else if(c == '/') {
                        if (hadStar) {
                            hadStar = false;
                            inComment = false;
                            seenFirstComment = true;
                        } else {
                            if (!seenFirstComment) {
                                firstComment.append(c);
                            }
                        }
                    } else {
                        if (hadStar && !seenFirstComment) {
                            firstComment.append('*');
                        }
                        hadStar = false;
                        if (!seenFirstComment) {
                            firstComment.append((char)c);
                        }
                    }
                } else if (inSingleQuotes) {
                    if (c == '\\') {
                        if (hadBackSlash) {
                            out.write('\\');
                            out.write('\\');
                            hadBackSlash = false;
                        } else {
                            hadBackSlash = true;
                        }
                    } else if (c == '\'') {
                        if (hadBackSlash) {
                            out.write('\\');
                            hadBackSlash = false;
                        } else {
                            inSingleQuotes = false;
                        }
                        out.write('\'');
                    } else if (c == '\r' || c == '\n') {
                        byte[] bb = out.toByteArray();
                        System.err.println(new String(bb));
                        System.err.println();
                        throw new PreprocessException("Unterminated single quote in file");
                    } else {
                        if (hadBackSlash) {
                            out.write('\\');
                            hadBackSlash = false;
                        }
                        out.write(c);
                    }
                } else if (inString) {
                    if (c == '\\') {
                        if (hadBackSlash) {
                            out.write('\\');
                            out.write('\\');
                            hadBackSlash = false;
                        } else {
                            hadBackSlash = true;
                        }
                    } else if (c == '"') {
                        if (hadBackSlash) {
                            out.write('\\');
                            hadBackSlash = false;
                        } else {
                            inString = false;
                        }
                        out.write('"');
                    } else if (c == '\r' || c == '\n') {
                        byte[] bb = out.toByteArray();
                        System.err.println(new String(bb));
                        System.err.println();
                        throw new PreprocessException("Unterminated string in file");
                    } else {
                        if (hadBackSlash) {
                            out.write('\\');
                            hadBackSlash = false;
                        }
                        out.write(c);
                    }
                } else if (inDirective) {
                    if (c == '\r' || c == '\n') {
                        inDirective = false;
                        String[] tokens = Preprocessor.tokenizeRow(directiveBuffer.toString());
    //                    if (tokens.length < 2 || tokens.length > 3) {
    //                        for (String token : tokens) {
    //                            System.err.println(token);
    //                        }
    //                        throw new PreprocessException("Bad preprocessor directive");
    //                    }
                        if (tokens[0].equals("define")) {
                            defines.put(tokens[1], (tokens.length == 3) ? tokens[2] : null);
                        }
    //                        throw new PreprocessException("Invalid/unsupported " +
    //                                "preprocessor directive '" + tokens[0] + "'");
                        directiveBuffer.setLength(0);
                    } else {
                        directiveBuffer.append((char)c);
                    }
                } else {
                    if (c == '/') {
                        if (hadSlash) {
                            out.write('/');
                        }
                        hadSlash = true;
                    } else if (c == '*') {
                        if (hadSlash) {
                            inComment = true;
                            hadSlash = false;
                        } else {
                            out.write(c);
                        }
                    } else if (c == '\'') {
                        if (hadSlash) {
                            out.write('/');
                        }
                        hadSlash = false;
                        out.write(c);
                        inSingleQuotes = true;
                    } else if (c == '"') {
                        if (hadSlash) {
                            out.write('/');
                        }
                        hadSlash = false;
                        out.write(c);
                        inString = true;
                    } else if (c == '#') {
                        if (defines == null) {
                            throw new PreprocessException("Unexpected preprocessor directive");
                        }
                        inDirective = true;
                    } else {
                        if (hadSlash) {
                            out.write('/');
                        }
                        hadSlash = false;
                        out.write(c);
                        // Only whitespace allowed before first comment:
                        if (c != ' ' && c != '\t' && c != '\r' && c != '\n') {
                            seenFirstComment = true;
                        }
                    }
                }
            }
            if (hadSlash) {
                out.write('/');
            }
            if (hadStar) {
                out.write('*');
            }
            if (inString) {
                throw new PreprocessException("Unterminated string at the end of file");
            }
            if (inComment) {
                throw new PreprocessException("Unterminated comment at the end of file");
            }
    //        byte[] bb = out.toByteArray();
    //        System.err.println(new String(bb));
    //        System.err.println();
            return out;
        }

    public static String[] tokenizeRow(String row)
            throws PreprocessException
    {
        String[] tokens = row.split("[ \t]");
        int numLiveTokens = 0;
        for (int i = 0; i < tokens.length; i++)
        {
            if (tokens[i] != null && tokens[i].length() > 0)
                ++numLiveTokens;
        }
        String[] liveTokens = new String[numLiveTokens];
        int next = 0;
        for (int i = 0; i < tokens.length; i++)
        {
            if (tokens[i] != null && tokens[i].length() > 0)
                liveTokens[next++] = tokens[i];
        }
        return liveTokens;
    }

}
